#pragma once
#include "Index.hpp"
#include <iostream>
#include <vector>
#include <string>
#include "util.hpp"
#include <unordered_map>
#include <jsoncpp/json/json.h>
#include <algorithm>
#include <ctype.h>
#include <unicode/brkiter.h>
#include <unicode/unistr.h>
#include<memory>

namespace Search_ns
{

    struct InvertedElems
    {
        uint64_t doc_id;
        int weight;
        std::vector<std::string> words;
        InvertedElems() : doc_id(0), weight(0) {}
    };

    class Searcher
    {
    private:
        Index_ns::Index *_index;

    public:
        Searcher() {};
        ~Searcher() {};

    public:
        void InitIndex(const std::string &input_root_path)
        {
            // 0.获取索引单例
            _index = Index_ns::Index::GetInstance();
            if (!_index)
                LOG(FATAL, "获取单例失败...");

            LOG(INFO, "获取单例成功");
            // 1.构建索引
            LOG(INFO, "开始构建索引...");
            _index->BulidIndex(input_root_path);
            LOG(INFO, "构建索引成功...");
        }

        void Search(const std::string &query, std::string *json_result)
        {
            // 0.将用户输入的字符串进行分词
            std::vector<std::string> query_words;
            Util_ns::Jieba_Util::CutString(query, &query_words);

            // 1.分词之后，根据关键字开始查倒排
            std::unordered_map<uint64_t, InvertedElems> _Inverted_results_mp;
            std::vector<InvertedElems> _Inverted_results_arr;

            // 1.1将查到的倒排结果放入哈希表中，起到一个去重的作用，我们不希望拿到多个相同的文档
            for (auto &query_word : query_words)
            {
                boost::to_lower(query_word);

                Index_ns::InvertList *_inverlist = _index->InvertIndex(query_word);

                if (_inverlist == nullptr) // 没查到这个关键字
                    continue;

                for (auto &elem : (*_inverlist)) // 遍历刚刚获取到的倒排拉链中的元素
                {
                    InvertedElems &_elem = _Inverted_results_mp[elem.id]; // 不存在就创建存在就获取
                    _elem.weight += elem.weight;
                    _elem.doc_id = elem.id;
                    _elem.words.emplace_back(elem.word);
                }
            }

            // 1.2将哈希表中的元素copy到数组中，并按照权值排序
            for (auto &pair : _Inverted_results_mp)
            {
                _Inverted_results_arr.emplace_back(std::move(pair.second));
            }

            // 1.2.1将数组中的倒排元素按照weight降序排序
            sort(_Inverted_results_arr.begin(), _Inverted_results_arr.end(), [](const InvertedElems &a, const InvertedElems &b) -> bool
                 { return a.weight > b.weight; });

            // 2.根据倒排拿出来的文档id,进行正排索引拿到文档，然后构建json串给用户返回
            Json::Value root;

            std::string word_s;
            for (auto &s : query_words)
            {
                word_s += s;
                word_s += " ";
            }
            word_s[word_s.size() - 1] = 0;
            for (auto &elem : _Inverted_results_arr)
            {
                Index_ns::DocInfo *doc = _index->ForwardIndex(elem.doc_id);
                if (doc == nullptr) // 没有查到该id对应的文档
                    continue;

                Json::Value sub;
                sub["title"] = doc->title;
                sub["abstract"] = GetAbstractImproved(doc->content, elem.words[0]); //?
                sub["url"] = doc->url;

                // for debug will delete
                sub["weight"] = elem.weight;
                sub["id"] = (int)elem.doc_id;
                root.append(sub);
            }

            Json::StyledWriter writer;
            if (!root.isNull())
                (*json_result) = writer.write(root);
        }

    private:
        std::string GetAbstractImproved(std::string &content, const std::string &key)
        {
            // 0. 初始化错误状态码
            UErrorCode status = U_ZERO_ERROR;
            LOG(DEBUG,"key is %s",key.c_str());
            // 1. 初始化ICU环境
            icu::Locale locale("en_US");

            // 2. 拿到句子边界分析器
            std::unique_ptr<icu::BreakIterator> bi(icu::BreakIterator::createSentenceInstance(locale, status));
            if (U_FAILURE(status))
            {
                return "Error creating BreakIterator";
            }

            std::unique_ptr<icu::BreakIterator> bw(icu::BreakIterator::createWordInstance(locale, status));

            icu::UnicodeString uContent = icu::UnicodeString::fromUTF8(content);
            icu::UnicodeString uContent_lower(uContent);
            uContent_lower.toLower(locale);

            bi->setText(uContent);
            bw->setText(uContent);

            // 3. 找到key第一次出现的位置
            int32_t pos = uContent_lower.indexOf(icu::UnicodeString::fromUTF8(key));
            if (pos == std::string::npos)
            { // 理论上不可能出现，没有查找到关键词在文档中的位置
                return "None1";
            }
            std::string key_; 
            icu::UnicodeString keyunicode =  uContent.tempSubStringBetween(pos,pos+5);
            keyunicode.toUTF8String(key_);
            LOG(DEBUG,"pos after 5 is %s",key_.c_str());

            // 4. 向前查找最近的边界
            int32_t start = bi->preceding(pos + 1); // 直接使用pos作为参数
            if (start == icu::BreakIterator::DONE)
            {
                start = 0;
            }
            //for debug
      
            // 5. 向后查找下一个边界
            int32_t end = bi->next(2); // 获取下一个边界
            
            if(end != icu::BreakIterator::DONE)
            {
                end = bw->following(end);
                end = bw->next(2);
            }
            end = std::min(content.length(),static_cast<size_t>(end));


            if(end <= start)
            {
                LOG(ERROR,"start is %d and end is %d",start,end); 
                return "None2";
            }
            // 提取摘要
            icu::UnicodeString abstract = uContent.tempSubStringBetween(start, end);
            std::string result;
            abstract.toUTF8String(result);

            result += "...";
            LOG(DEBUG,"result is %s",result.c_str());
            return result;
        }
    };
}
